#include "Node.h"
#include "System.h"
#include "Mailer.h"
#include "SendClockMessage.h"
#include "SyncMessage.h"

Node::Node(int id, System* sys, Mailer* mailer, int initClock)
{
	m_id = id;
	m_system = sys;
	m_mailer = mailer; 
	m_node = m_system->createThread(algorithm, this);
	m_clock = initClock;
	m_neighbours = new NEIGHBOURS;
	m_shouldRun = true;
}

Node::~Node(void)
{
	m_node->exit(); // Thread will be deleted by the Scheduler
	delete m_neighbours;
}

void Node::addNeighbour(int neighbourID)
{
	m_neighbours->insert(NEIGHBOURS::value_type(neighbourID, INITCLOCK));
}

void* Node::algorithm(void* arg)
{
	Node* node = (Node*)arg;

	Message* m;
	while(node->m_shouldRun)
	{
		node->sendClockToAll();
		int id = node->getThreadID();
		int size = node->m_mailer->getMailboxSize(id);

		for(int u=0;u<size;u++)
		{
			//get message
			m = node->m_mailer->recieve(id);

			//run message
			m->run(node);
			//m->print();
		}

		int minimal = node->m_clock;
		int maximal = node->m_clock;
		bool sync = true;

		NEIGHBOURS::iterator it;
		for(it=node->m_neighbours->begin();it!=node->m_neighbours->end();it++)
		{
			if(node->m_system->getNodeStatus(it->first))
			{
				int clock = it->second;
				sync = sync && (clock == node->m_clock);
				if(clock < minimal) minimal = clock;
				if(clock > maximal) maximal = clock;				
			}
		}

		if(sync) 
			node->m_clock++;
		else if(node->m_clock == minimal)
			node->m_clock = maximal;
			
		sleep(1);
	}
}

int Node::getID()
{
	return m_id;
}

int Node::getThreadID()
{
	return m_node->getID();
}

void Node::setClock(int neighbourID, int clock)
{
	(*m_neighbours)[neighbourID] = clock;
}

void Node::setOwnClock(int clock)
{
	m_clock = clock;
	m_system->SendSyncMsg(getThreadID(), getThreadID());
	m_system->startTimer();
}

int Node::getClock()
{
	return m_clock;
}

void Node::sendClockToAll()
{
	NEIGHBOURS::iterator it;
	for(it=m_neighbours->begin();it!=m_neighbours->end();it++)
	{
			int dest = it->first;
			sendClock(dest);
	}
}

void Node::syncMessageProc(SyncMessage* msg)
{
	//printf("SyncMesg received on node %d\n", getThreadID()); 
	msg->m_timeSteps++;
	if(msg->m_creator == getThreadID())
	{
		int myFlag = (*msg->m_flags)[getThreadID()];
//		printf("%d is the creator, my flag is %d\n", getThreadID(), myFlag);
		if(myFlag == 0)
		{
				//i am the creator and the msg wasnt sent yet
				if(this->isInSync())
				{
					(*msg->m_flags)[getThreadID()] = 1;
					msg->m_course->push(getThreadID());
					
					NEIGHBOURS::iterator it;
					for(it=m_neighbours->begin();it!=m_neighbours->end();it++)
					{
						int dest = it->first;
						msg->setDestination(getThreadID(),dest);
						if(!m_mailer->send(dest, msg))
						{
							(*msg->m_flags)[dest] = 2;
						}
						else return;
					}
					msg->setDestination(getThreadID(),0);
					m_mailer->send(0,msg);	
					return;
				}
				else
				{
					m_mailer->send(getThreadID(), msg);
					return;
				}
		}
		else
		{
			//printf("//i am the creator and the msg was sent already - i received it from other node\n");
			map<int,int>::iterator flag_it;
			for(flag_it=msg->m_flags->begin();flag_it!=msg->m_flags->end();flag_it++)
			{
				if(flag_it->second == -1)
				{
//					printf("//some neighbour is unsyncd - create a new msg %d unsyncd\n", flag_it->first);
					m_system->SendSyncMsg(getThreadID(), getThreadID(),msg->m_timeSteps);
					return;
				}
			}
//			printf("//all nodes so far are syncd\n");
							
			NEIGHBOURS::iterator it;
			for(it=m_neighbours->begin();it!=m_neighbours->end();it++)
			{
				int neighbour = it->first;
				if((*msg->m_flags)[neighbour] == 0)
				{
					//this neighbour didnt receive the message yet.
					msg->m_course->push(getThreadID());
					msg->setDestination(getThreadID(),neighbour);
					if(!m_mailer->send(neighbour, msg))
					{
						(*msg->m_flags)[neighbour] = 2;
					}
					else return;
				}
			}
//			printf("all the neighbours got the msg - send it to the shell - %d\n", getThreadID());
			msg->setDestination(getThreadID(),0);
			m_mailer->send(0,msg);	
		}
	}	
	else
	{
		//this node isnt the creator
		if(this->isInSync())
		{
			(*msg->m_flags)[getThreadID()] = 1;
			NEIGHBOURS::iterator it;
			for(it=m_neighbours->begin();it!=m_neighbours->end();it++)
			{
				int neighbour = it->first;
				if((*msg->m_flags)[neighbour] == 0)
				{
					//this neighbour didnt receive the message yet.
					msg->setDestination(getThreadID(),neighbour);
					msg->m_course->push(getThreadID());
					if(!m_mailer->send(neighbour, msg))
					{
						(*msg->m_flags)[neighbour] = 2;
					}
					else return;
				}
			}
			//all neighbours got the message - send back to sender
			
			int sendTo = msg->m_course->top();
			msg->m_course->pop();
			msg->setDestination(getThreadID(),sendTo);
			m_mailer->send(sendTo,msg);
			return;
		}
		else
		{
			//i am not syncd - send the msg back to its creator
			(*msg->m_flags)[getThreadID()] = -1;
			msg->setDestination(getThreadID(),msg->m_creator);
			m_mailer->send(msg->m_creator,msg);
		}
	}
}

bool Node::isInSync()
{
	bool sync = true;
	
	NEIGHBOURS::iterator it;
	for(it=m_neighbours->begin();it!=m_neighbours->end();it++)
	{
		if(m_system->getNodeStatus(it->first))
		{
			int clock = it->second;
			sync = sync && (clock == m_clock || clock - 1 == m_clock || clock + 1 == m_clock);	
		}
	}
	return sync;
}

int Node::getTimeRequest()
{
	int neighbourCounter = 0;
	NEIGHBOURS::iterator it;
	for(it=m_neighbours->begin();it!=m_neighbours->end();it++)
	{
		if(m_system->getNodeStatus(it->first)) neighbourCounter++;
	}
	int messagesCounter = m_mailer->getMailboxSize(getThreadID());
	
	return messagesCounter + neighbourCounter;
}

void Node::sendClock(int id)
{
	Message* m = new SendClockMessage(getThreadID(), id, m_clock, m_id);
	m_mailer->send(id, m);
}


